package cc.shacocloud.luanniao.web.controller.interceptor


import cc.shacocloud.luanniao.core.exception.BadRequestException
import cc.shacocloud.luanniao.core.exception.ForbiddenException
import cc.shacocloud.luanniao.core.model.mo.UserNamespaceMo
import cc.shacocloud.luanniao.core.service.NamespaceService
import cc.shacocloud.luanniao.restful.RequestMappingHandlerChain
import cc.shacocloud.luanniao.restful.RequestMappingHandlerInterceptor
import cc.shacocloud.luanniao.restful.RequestMappingHandlerInterceptorAdapter
import cc.shacocloud.luanniao.web.controller.interceptor.AuthInterceptor.Companion.currentUser
import kotlinx.coroutines.reactor.awaitSingleOrNull
import kotlinx.coroutines.reactor.mono
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.core.annotation.AnnotatedElementUtils
import org.springframework.stereotype.Service
import org.springframework.web.method.HandlerMethod
import org.springframework.web.reactive.HandlerAdapter
import org.springframework.web.reactive.HandlerResult
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono


/**
 * 用户命名空间权限拦截器
 *
 * 访问的请求路径以 /api/namespace/{namespaceId} 为前缀
 *
 * 基于 [HandlerAdapter] 实现得认证拦截器。见 [RequestMappingHandlerInterceptorAdapter]
 * @author 思追(shaco)
 */
@Service
class UserNamespacePermissionInterceptor(
    val namespaceService: NamespaceService,
) : RequestMappingHandlerInterceptor {

    companion object {
        private val log: Logger = LoggerFactory.getLogger(UserNamespacePermissionInterceptor::class.java)

        /**
         * 上问下中存储 [UserNamespaceMo] 的键
         */
        val USER_NAMESPACE_CONTEXT_KEY =
            "${UserNamespacePermissionInterceptor::class.java.canonicalName}.userNamespaceMo"

        /**
         * 当前用户在命名空间的权限信息
         */
        fun ServerWebExchange.currentUserNamespaceMo(): UserNamespaceMo {
            return attributes[USER_NAMESPACE_CONTEXT_KEY] as UserNamespaceMo? ?: throw ForbiddenException()
        }
    }

    override fun handle(
        exchange: ServerWebExchange,
        handler: HandlerMethod,
        chain: RequestMappingHandlerChain,
    ): Mono<HandlerResult> = mono {

        // 获取当前接口允许的用户认证角色访问权限，未使用注解则不进行权限处理
        val needUserNamespaceRole = handler.getMethodAnnotation(NamespacePermission::class.java)?.role
            ?: AnnotatedElementUtils.findMergedAnnotation(handler.beanType, NamespacePermission::class.java)?.role
        if (needUserNamespaceRole == null) return@mono chain.next(exchange, handler).awaitSingleOrNull()

        val path = exchange.request.path.value()
        if (!path.startsWith("/api/namespace/")) {
            if (log.isWarnEnabled) {
                log.warn(
                    "${handler.shortLogMessage} 上使用 @NamespacePermission 相关的注解但是声明的路径不满足格式：" +
                        "必须以 /api/namespace/{namespaceId} 为前缀，实际的请求路径：$path"
                )
            }
            return@mono chain.next(exchange, handler).awaitSingleOrNull()
        }

        // 解析命名空间
        val namespaceIdStr = path.removePrefix("/api/namespace/").substringBefore("/")
        val namespaceId = namespaceIdStr.toLongOrNull()
        if (namespaceId == null) {
            log.warn(
                "${handler.shortLogMessage} 的实际请求路径中 {namespaceId} 对应的值 $namespaceIdStr 无法转为数值：$path"
            )
            throw BadRequestException()
        }

        // 获取当前访问用户信息
        val userPo = exchange.currentUser()
        val userId = requireNotNull(userPo.id)
        val userNamespaceMo = namespaceService.findUserNamespace(namespaceId, userId)

        // 当前用户绑定的命名空间权限大于需要的权限则通过
        if (userNamespaceMo.role.level >= needUserNamespaceRole.level) {
            exchange.attributes[USER_NAMESPACE_CONTEXT_KEY] = userNamespaceMo
            return@mono chain.next(exchange, handler).awaitSingleOrNull()
        }

        throw ForbiddenException()
    }


    override fun getOrder(): Int {
        return 0
    }

}